/*
 * IBM Corporation.
 * Copyright (c) 2014 All Rights Reserved.
 */

package com.ibm.iisp.dataie.service;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Map;

import javax.persistence.EntityManager;

import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.ibm.iisp.auth.util.SecurityUtils;
import com.ibm.iisp.common.exception.I18nAppException;
import com.ibm.iisp.common.util.FileStoreUtils;
import com.ibm.iisp.dataie.model.DataIETable;
import com.ibm.iisp.dataie.vo.DataExportRequest;

/**
 * 类作用：
 * @author Johnny@cn.ibm.com
 * 使用说明：
 * 		String fname = exporter.init();
 *		exporter.preProcedure();
 *		exporter.doExport();
 *		exporter.finish();
 *
 */
public abstract class AbstractDataExporter {
	Logger log = LoggerFactory.getLogger(getClass());
	protected EntityManager dao;
	protected DataExportRequest dataExportReq;
	protected Map<String, ?> params;

	/**
	 * @param dao
	 */
	public AbstractDataExporter(DataExportRequest dataExportReq, EntityManager dao) {
		super();
		this.dao = dao;
		this.dataExportReq = dataExportReq;
	}

	public static final String DATA_EXP_FILE_ROOT = FileStoreUtils.TEMP_PATH + "/iisp-data-ie/exports";

	public abstract String init() throws I18nAppException;

	public void preProcedure() {
		DataIETable tb = dao.find(DataIETable.class, dataExportReq.getTableName());
		if (tb.getExpProcedure() != null) {
			String procState = "{call " + tb.getExpProcedure() + "}";
			if (procState.indexOf("${") > 0) {
				procState = DataIEUtils.replaceVariables(dataExportReq.getFilterParams(), procState);
			}
			log.info(procState);
			dao.createNativeQuery(procState).executeUpdate();
		}
	}

	protected void parseFilterParams() {
		if (dataExportReq.getFilter() != null) {
			DataIETable tb = dao.find(DataIETable.class, dataExportReq.getTableName());
			@SuppressWarnings("unchecked")
			Map<String, Object> fparams = (Map<String, Object>) DataIEUtils.parseParams(dataExportReq.getFilter(), tb.getDefaultFilter());
			if (fparams != null && !fparams.isEmpty()) {
				if (dataExportReq.getFilterParams() == null) {
					dataExportReq.setFilterParams(fparams);
				} else {
					dataExportReq.getFilterParams().putAll(fparams);
				}
			}
		}
	}
	/*
	 * (non-Javadoc)
	 * 
	 * @see com.ibm.iisp.dataie.service.AbstractDataExporter#doExport(com.ibm.iisp.dataie.model.DataExportRequest)
	 */
	public void doExport() throws I18nAppException {
		String colNames = getExportCols();
		StringBuilder sql = new StringBuilder(1000);
		sql.append("SELECT ").append(colNames);
		sql.append(" FROM ").append(dataExportReq.getTableName());
		applyFilter(sql);
		applyOrder(sql);
		Connection con = dao.unwrap(Connection.class);
		ResultSet rs = null;
		log.debug("SQL: {}", sql);
		try (PreparedStatement ps = con.prepareStatement(sql.toString())) {
			rs = ps.executeQuery();

			int colCount = rs.getMetaData().getColumnCount();
			while (rs.next()) {
				exportRow(rs, colCount);
			}
		} catch (SQLException e) {
			log.error("Failed to get data from db.", e);
			throw new I18nAppException("IISP_DATA_DB_ERROR", new String[] { e.getMessage() },
				"Error to write file:" + e.getMessage(), e);
		} finally {
			if (rs != null) {
				try {
					rs.close();
				} catch (SQLException e1) {
					log.error("Error to close resultset", e1);
				}
			}
		}

	}

	protected abstract String getExportCols();

	protected abstract void exportRow(ResultSet rs, int colCount) throws SQLException, I18nAppException;

	public abstract void finish();

	protected void applyOrder(StringBuilder sql) {
		DataIETable tb = dao.find(DataIETable.class, dataExportReq.getTableName());
		if (tb.getOrderBy() != null) {
			sql.append(" ").append(tb.getOrderBy());
		}
	}
	/**
	 * @param sql
	 */
	protected void applyFilter(StringBuilder sql) {
		// 过滤条件中变量置换
		if (dataExportReq.getFilterParams() != null) {
			for (Map.Entry<String, Object> entry : dataExportReq.getFilterParams().entrySet()) {
				String ph = "${" + entry.getKey() + "}";
				while (dataExportReq.getFilter().contains(ph)) {
					dataExportReq.setFilter(dataExportReq.getFilter().replace(ph, (String) entry.getValue()));
				}
			}
		}
		// 过滤条件添加到SQL
		DataIETable tb = dao.find(DataIETable.class, dataExportReq.getTableName());
		sql.append(" t0");
		if (StringUtils.isNotBlank(dataExportReq.getFilter())) {
			sql.append(" WHERE ").append(dataExportReq.getFilter());
		}
		// 分行增加过滤
		if (StringUtils.isNotBlank(tb.getNodeDataFilter())) {
			if (!SecurityUtils.getInstance().isLoginUserFromHeadquarter()) {
				log.debug("need to apply node data filter: {}", tb.getNodeDataFilter());
				String nodeFilter = DataIEUtils.replaceVariables(tb.getNodeDataFilter());
				if (sql.indexOf(" WHERE ") < 0) {
					sql.append(" WHERE ");
				} else {
					sql.append(" AND ");
				}
				sql.append(nodeFilter);
			}
		}
		// 处理增量数据过滤
		if (Boolean.TRUE.equals(dataExportReq.getIsExportIncOnly())) {
			if (tb.getIncOnlyFilter() == null) {
				Object dt = dataExportReq.getFilterParams().get("dataDate");
				if (dt == null) {
					dt = dataExportReq.getFilterParams().get("currentDataDate");
				}
				if (dt == null) {
					dt = dataExportReq.getFilterParams().get("DATA_DATE");
				}
				applyIncFilter(sql, tb, (String) dt);
			} else {
				if (sql.indexOf(" WHERE ") < 0) {
					sql.append(" WHERE ");
				} else {
					sql.append(" AND ");
				}
				sql.append(tb.getIncOnlyFilter());
			}
		}
	}

	/**
	 * @param sql
	 * @param tb
	 */
	public static void applyIncFilter(StringBuilder sql, DataIETable tb, String dataDate) {
		if (sql.indexOf(" WHERE ") > 0 || sql.indexOf(" where ") > 0) {
			sql.append(" AND ");
		} else {
			sql.append(" WHERE ");
		}
		sql.append("not exists (select 1 from ").append(tb.getCode()).append(" t1 where 1=1");
		for (String colName : tb.getKeyColumns().split(",")) {
			if ("DATA_DATE".equals(colName.trim())) {
				sql.append(" and t1.DATA_DATE < '").append(dataDate).append("'");
			} else {
				sql.append(" and t0.").append(colName).append(" = t1.").append(colName);
			}
		}
		sql.append(")");
	}

}
